package com.woniu.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.woniu.entity.Bank;
import com.woniu.exception.controllerException.BankAccountNotFoundException;
import com.woniu.exception.controllerException.BankFileException;
import com.woniu.exception.controllerException.CellTypeErrorException;
import com.woniu.exception.controllerException.MoneyOutOfRangeException;
import com.woniu.mapper.BankMapper;
import com.woniu.service.IBankService;
import com.woniu.util.EmptyUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author hyl,fzn
 * @since 2022-08-28
 *
 * 事务注解 Transactional(rollbackFor = Exception.class)
 */
@Transactional(rollbackFor = Exception.class)
@Service
@Slf4j
public class BankServiceImpl extends ServiceImpl<BankMapper, Bank> implements IBankService {
    @Autowired
    private BankMapper bm;

    /*下面的方法内部可以做优化，因为判断的时候都是差不多的逻辑,
    所以可以考虑将其提出来作为一个工具类的静态方法集中调用*/

    @Override
    public void insertBank(Bank bank) {
        if(bank!=null && bank.getBankId()!=null && bank.getBankId()>0
                && bank.getLeftMoney()!=null
                && (bank.getLeftMoney().compareTo(new BigDecimal("0.00")))>=0
        ){
            bm.insertBank(bank);
        }

    }


    @Override
    public void updateBank(Bank bank) {
        if(bank!=null && bank.getBankId()!=null && bank.getBankId()>0
                && bank.getLeftMoney()!=null
                && (bank.getLeftMoney().compareTo(new BigDecimal("0.00")))>=0
        ){
            bm.updateBank(bank);
        }
    }

    /**
     *
     * @param bank
     * @return 如果查询不到则返回空
     * 在controller调用的时候要注意判断是否为空，以及要抛出相应的异常
     */
    @Override
    public Bank selectOneBank(Bank bank) {
        if(bank!=null && bank.getBankId()!=null && bank.getBankId()>0
        ){
            return bm.selectOneBank(bank);
        }
        return null;
    }

    @Override
    public Bank selectBankById(Integer bankId){
        if (bankId != null && bankId>0){
            //  如果不存在，需要捕捉，null
            if(bm.selectBankById(bankId) == null){
                throw new BankAccountNotFoundException("账户不存在，或已销毁");
            }
            return bm.selectBankById(bankId);
        }else{
            // 自定义异常，账号不存在
            throw new BankAccountNotFoundException("银行账户为空");
        }
    }

    @Override
    public void deleteOneBank(Bank bank) {
        if(bank!=null && bank.getBankId()!=null && bank.getBankId()>0){
            bm.deleteBank(bank);
        }
    }

    @Override
    public BigDecimal debitMoney(Bank bank, BigDecimal targetdeBitMoeny) {
        Double afterDebit = bank.getLeftMoney().subtract(targetdeBitMoeny).doubleValue();
        Double realAfterDebit = afterDebit< 0 ? 0.00 : afterDebit;
        if(realAfterDebit==0.00) {
            throw new MoneyOutOfRangeException("银行账户可用余额不足");
        }else{
            return new BigDecimal(realAfterDebit);
        }
    }

    /**
     *
     * @param pageNum 查询的页数
     * @param pageSize
     * @param bank
     * @return
     */
    @Override
    public Page<Bank> getAnyBankAccountsWithPage(Integer pageNum,Integer pageSize,Bank bank){
        QueryWrapper wrapper =  getBankWrapper(bank);
        Page page = new Page(pageNum,pageSize);

        return bm.getByAnyBankWithPage(page,wrapper);
    }

    @Override
    public List<Bank> getBankByCondition(Bank bank) {
        QueryWrapper wrapper = getBankWrapper(bank);
        return bm.getByCondition(wrapper);
    }

    /**
     * 用于组装QueryWrapper
     * @param bank 实体类
     * @return 返回一个QueryWrapper，用于查询
     */
    private static QueryWrapper getBankWrapper(Bank bank){
        QueryWrapper<Bank> wrapper = new QueryWrapper<>();
        // 如果bank为空，默认查询全部，恒等式（简单sql语句注入）1=1
        if(EmptyUtil.isEmpty(bank)){
            wrapper.eq("1",'1');
            return wrapper;
        }
        // 根据银行账户id进行查询
        if(EmptyUtil.isNoEmpty(bank.getBankId())){
            if ("eq".equals(bank.getBankIdJudgeType())) {
                wrapper.eq("bank_id",bank.getBankId());
            }else if ("gt".equals(bank.getBankIdJudgeType())){
                wrapper.gt("bank_id",bank.getBankId());
            }else if ("lt".equals(bank.getBankIdJudgeType())){
                wrapper.lt("bank_id",bank.getBankId());
            }
        }
        // 根据银行账户余额进行查询
        if(EmptyUtil.isNoEmpty(bank.getBankId())){
            if ("gt".equals(bank.getLeftMoneyJudgeType())){
                wrapper.gt("left_money",bank.getLeftMoney());
            }else if ("lt".equals(bank.getLeftMoneyJudgeType())){
                wrapper.lt("left_money",bank.getLeftMoney());
            }
        }
        return wrapper;
    }
    
    @Transactional(readOnly = false,rollbackFor = Exception.class)
    @Override
    public boolean batchImport(String fileName, MultipartFile file) throws Exception {
        // 用于判断excel表格是否为空
        boolean notNull = false;
        List<Bank> bankList = new ArrayList<>();
        if (!fileName.matches("^.+\\.(?i)(xls)$")
                && !fileName.matches("^.+\\.(?i)(xlsx)$")){
            throw new BankFileException("上传的文件格式不正确");
        }
        boolean isExcel2003 = true;
        if (fileName.matches("^.+\\.(?i)(xlsx)$")) {
            isExcel2003 =false;
        }
        InputStream is = file.getInputStream();
        Workbook wb = null;
        if(isExcel2003){
            wb = new HSSFWorkbook(is);
        }else{
            wb = new XSSFWorkbook(is);
        }
        Sheet sheet = wb.getSheetAt(0);
        if(sheet!=null){
            notNull = true;
        }


        /*行号和列号都是从0开始
        * 但是最后一行和最后一列的数字有区别
        * 对于行来说，最后一行的行号加1等于excel表的行数
        * 对于列来说，最后一列的列号即等于excel表的列数。
        * 但是这并不代表最后一个列号所在的列里有数据
        * */
        int firstRowNum = sheet.getFirstRowNum();
        int lastRowNum = sheet.getLastRowNum();

        row:
        for(int r=firstRowNum;r <= lastRowNum; r++){
            Bank bank = new Bank();
            Row row = sheet.getRow(r);
            // 判断第一行是否为空
            if(row==null){
                continue;
            }
            // 获取第一列的序号
            int firstCellNum = row.getFirstCellNum();
            // 获取最后一列的序号
            int lastCellNum = row.getLastCellNum();
            // 判断第一行是否为中文(规定)
            if(r == sheet.getFirstRowNum()){
                // 如果最终为1，证明一行的数据都符合规范
                int firstRowType = 1;
                column:
                for(int c = firstCellNum;c <=lastCellNum-1; c++){
                    // 不为文本类型
                    if(row.getCell(c).getCellType() != CellType.STRING){
                        firstRowType *= 0;
                    }
                    if(firstRowType == 0){
                        throw new CellTypeErrorException("第一行部分数据不是文本格式，请重新尝试");
                    }
                }

            }
            // 如果不是第一行的，就进行赋值
            else{

                for(int c = firstCellNum;c <=lastCellNum-1; c++){
                    if("银行账户ID".equals(getExcelFirstRowEachCellValue0(wb,0,c))){
                        Cell cell = row.getCell(c);
                        if(cell.getCellType() != CellType.NUMERIC){
                            throw new CellTypeErrorException("第"+ c +"行第"+ c + "列的数据类型有误");
                        }
                        bank.setBankId((int)cell.getNumericCellValue());
                    }
                    if("剩余金额".equals(getExcelFirstRowEachCellValue0(wb,0,c))){
                        Cell cell = row.getCell(c);
                        if(cell.getCellType() != CellType.NUMERIC){
                            throw new CellTypeErrorException("第"+ c +"行第"+ c + "列的数据类型有误");
                        }
                        bank.setLeftMoney(new BigDecimal(cell.getNumericCellValue()).setScale(2,BigDecimal.ROUND_HALF_DOWN));
                    }
                    }
                bankList.add(bank);
            }

        }
        if(bankList.size() == 0){
            return notNull;
        }

        bankList.forEach((b)->{
            bm.insertBank(b);
        });

        return !notNull;
    }

    /**
     *  获取对应表格的第一行对应位置的信息
     * @param wb
     * @param sheetNum 表单代表数字
     * @param num 表格位置数字
     * @return
     */
    public static String getExcelFirstRowEachCellValue0(Workbook wb,int sheetNum, int num){
        Sheet sheet = wb.getSheetAt(sheetNum);
        // 获取第一行
        Row firstRow = sheet.getRow(0);
        Cell cell = firstRow.getCell(num);
        return cell.getStringCellValue();

    }
}
